{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1SgrstLXNbG_"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "k7gifg92NbG9"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dCMqzy7BNbG9"
      },
      "source": [
        "# DeepDream"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2yqCPS8SNbG8"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/generative/deepdream\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ru/tutorials/generative/deepdream.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ru/tutorials/generative/deepdream.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ru/tutorials/generative/deepdream.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8a2322303a3f"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XPDKhwPcNbG7"
      },
      "source": [
        "Это руководство содержит минимальную реализацию DeepDream, как описано в этом [сообщении в блоге](https://ai.googleblog.com/2015/06/inceptionism-going-deeper-into-neural.html) Александра Мордвинцева.\n",
        "\n",
        "DeepDream - это эксперимент, который визуализирует закономерности, полученные нейронной сетью. Подобно тому, как ребенок наблюдает за облаками и пытается интерпретировать случайные формы, DeepDream чрезмерно интерпретирует и усиливает узоры, которые он видит на изображении.\n",
        "\n",
        "Это делается путем пересылки изображения по нейронной сети с последующим вычислением градиента изображения по отношению к активациям определенного слоя. Затем изображение модифицируется, чтобы усилить эти активации, усилить паттерны, видимые сетью, и в результате получить образ, похожий на сон. Этот процесс получил название «Inceptionism»(ссылка на [InceptionNet](https://arxiv.org/pdf/1409.4842.pdf) и [фильм Начало](https://en.wikipedia.org/wiki/Inception)).\n",
        "\n",
        "Давайте посмотрим, как можно сделать нейронную сеть «мечтой» и улучшить сюрреалистические паттерны, которые она видит на изображении.\n",
        "\n",
        "![Dogception](https://www.tensorflow.org/tutorials/generative/images/dogception.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Sc5Yq_Rgxreb"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "g_Qp173_NbG5"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "import matplotlib as mpl\n",
        "\n",
        "import IPython.display as display\n",
        "import PIL.Image\n",
        "\n",
        "from tensorflow.keras.preprocessing import image"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wgeIJg82NbG4"
      },
      "source": [
        "## Выбор изображения для визуализации \"сна\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yt6zam_9NbG4"
      },
      "source": [
        "В этом уроке давайте будем использовать изображение [лабрадора](https://commons.wikimedia.org/wiki/File:YellowLabradorLooking_new.jpg)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0lclzk9sNbG2"
      },
      "outputs": [],
      "source": [
        "url = 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg'"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Y5BPgc8NNbG0"
      },
      "outputs": [],
      "source": [
        "# Загружаем изображение и читаем его в NumPy array.\n",
        "def download(url, max_dim=None):\n",
        "  name = url.split('/')[-1]\n",
        "  image_path = tf.keras.utils.get_file(name, origin=url)\n",
        "  img = PIL.Image.open(image_path)\n",
        "  if max_dim:\n",
        "    img.thumbnail((max_dim, max_dim))\n",
        "  return np.array(img)\n",
        "\n",
        "# Нормализуем изображение\n",
        "def deprocess(img):\n",
        "  img = 255*(img + 1.0)/2.0\n",
        "  return tf.cast(img, tf.uint8)\n",
        "\n",
        "# Выводим изображение\n",
        "def show(img):\n",
        "  display.display(PIL.Image.fromarray(np.array(img)))\n",
        "\n",
        "\n",
        "# Уменьшаем размер изображения для упрощения работы с ним\n",
        "original_img = download(url, max_dim=500)\n",
        "show(original_img)\n",
        "display.display(display.HTML('Image cc-by: <a \"href=https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg\">Von.grzanka</a>'))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F4RBFfIWNbG0"
      },
      "source": [
        "## Подготовка модели извлечения признаков"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cruNQmMDNbGz"
      },
      "source": [
        "Загрузите и подготовьте предварительно обученную модель классификации изображений. Вы будете использовать [InceptionV3](https://keras.io/applications/#inceptionv3), которая похожа на модель, изначально использовавшуюся в DeepDream. Обратите внимание, что любая [предварительно обученная модель](https://keras.io/applications/#models-for-image-classification-with-weights-trained-on-imagenet) будет здесь работать, однако вам придется настроить имена слоев."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GlLi48GKNbGy"
      },
      "outputs": [],
      "source": [
        "base_model = tf.keras.applications.InceptionV3(include_top=False, weights='imagenet')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Bujb0jPNNbGx"
      },
      "source": [
        "Идея DeepDream состоит в том, чтобы выбрать слой(или слои) и максимизировать «потери» таким образом, чтобы изображение все больше «возбуждало» слои. Сложность включенных функций зависит от выбранных вами слоев, то есть нижние слои создают штрихи или простые узоры, а более глубокие слои придают сложные функции изображениям или даже целым объектам."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qOVmDO4LNbGv"
      },
      "source": [
        "Архитектура InceptionV3 довольно большая(диаграмму архитектуры модели см. в [исследовательском репозитории TensorFlow](https://github.com/tensorflow/models/tree/master/research/inception)). Для DeepDream нас интересуют те слои, в которых свертки объединены. В InceptionV3 есть 11 таких слоев, которые имеют имена от «mixed0» до «mixed10». Использование разных слоев приведет к получению разных сказочных изображений. Более глубокие слои реагируют на элементы более высокого уровня (например, глаза и лица), тогда как более ранние слои реагируют на более простые элементы (такие как края, формы и текстуры). Не стесняйтесь экспериментировать со слоями, но имейте в виду, что более глубокие слои(с более высоким индексом) потребуют больше времени для обучения, поскольку вычисление градиента более глубокое."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "08KB502ONbGt"
      },
      "outputs": [],
      "source": [
        "# максимизируем активацию этих слоев\n",
        "names = ['mixed3', 'mixed5']\n",
        "layers = [base_model.get_layer(name).output for name in names]\n",
        "\n",
        "# создаем модель извлечения признаков\n",
        "dream_model = tf.keras.Model(inputs=base_model.input, outputs=layers)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sb7u31B4NbGt"
      },
      "source": [
        "## Расчет величины потерь\n",
        "\n",
        "Потери - это сумма активаций в выбранных слоях. Потери нормализованы на каждом слое, поэтому вклад более крупных слоев не перевешивает меньшие слои. Обычно потери - это величина, которую вы хотите минимизировать с помощью градиентного спуска. В DeepDream вы максимизируете эту потерю с помощью градиентного подъема."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8MhfSweXXiuq"
      },
      "outputs": [],
      "source": [
        "def calc_loss(img, model):\n",
        "  # Передаем изображение через модель, чтобы получить активации.\n",
        "  # Преобразуем изображение в пакет размером 1.\n",
        "  img_batch = tf.expand_dims(img, axis=0)\n",
        "  layer_activations = model(img_batch)\n",
        "  if len(layer_activations) == 1:\n",
        "    layer_activations = [layer_activations]\n",
        "\n",
        "  losses = []\n",
        "  for act in layer_activations:\n",
        "    loss = tf.math.reduce_mean(act)\n",
        "    losses.append(loss)\n",
        "\n",
        "  return  tf.reduce_sum(losses)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "k4TCNsAUO9kI"
      },
      "source": [
        "## Градиентный подъем\n",
        "\n",
        "После того, как вы рассчитали потери для выбранных слоев, осталось вычислить градиенты по отношению к изображению и добавить их к исходному изображению.\n",
        "\n",
        "Добавление градиентов к изображению усиливает узоры, видимые сетью. На каждом этапе вы будете создавать изображение, которое все больше и больше возбуждает активацию определенных слоев в сети.\n",
        "\n",
        "Метод, который делает это,  заключен в `tf.function`, для повышения производительности. Он использует `input_signature`, чтобы гарантировать, что функция не будет восстанавливаться для разных размеров изображения или значений `steps`/`step_size`. Подробности см. в [Руководство по конкретным функциям](../../guide/concrete_function.ipynb)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qRScWg_VNqvj"
      },
      "outputs": [],
      "source": [
        "class DeepDream(tf.Module):\n",
        "  def __init__(self, model):\n",
        "    self.model = model\n",
        "\n",
        "  @tf.function(\n",
        "      input_signature=(\n",
        "        tf.TensorSpec(shape=[None,None,3], dtype=tf.float32),\n",
        "        tf.TensorSpec(shape=[], dtype=tf.int32),\n",
        "        tf.TensorSpec(shape=[], dtype=tf.float32),)\n",
        "  )\n",
        "  def __call__(self, img, steps, step_size):\n",
        "      print(\"Tracing\")\n",
        "      loss = tf.constant(0.0)\n",
        "      for n in tf.range(steps):\n",
        "        with tf.GradientTape() as tape:\n",
        "          # Для этого нужны градиенты относительно `img`\n",
        "          # По умолчанию `GradientTape` следит только за `tf.Variable`s\n",
        "          tape.watch(img)\n",
        "          loss = calc_loss(img, self.model)\n",
        "\n",
        "        # Вычисляем градиент потерь по отношению к пикселям входного изображения.\n",
        "        gradients = tape.gradient(loss, img)\n",
        "\n",
        "        # Нормализуем градиенты\n",
        "        gradients /= tf.math.reduce_std(gradients) + 1e-8 \n",
        "        \n",
        "        # При градиентном подъеме «потери» максимизируются, \n",
        "        # так что входное изображение все больше «возбуждает» слои.\n",
        "        # Вы можете обновить изображение, напрямую добавив градиенты(потому что они одинаковой размерности!)\n",
        "        img = img + gradients*step_size\n",
        "        img = tf.clip_by_value(img, -1, 1)\n",
        "\n",
        "      return loss, img"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yB9pTqn6xfuK"
      },
      "outputs": [],
      "source": [
        "deepdream = DeepDream(dream_model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XLArRTVHZFAi"
      },
      "source": [
        "## Основной цикл"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9vHEcy7dTysi"
      },
      "outputs": [],
      "source": [
        "def run_deep_dream_simple(img, steps=100, step_size=0.01):\n",
        "  # Преобразовываем из uint8 в диапазон, ожидаемый моделью.\n",
        "  img = tf.keras.applications.inception_v3.preprocess_input(img)\n",
        "  img = tf.convert_to_tensor(img)\n",
        "  step_size = tf.convert_to_tensor(step_size)\n",
        "  steps_remaining = steps\n",
        "  step = 0\n",
        "  while steps_remaining:\n",
        "    if steps_remaining>100:\n",
        "      run_steps = tf.constant(100)\n",
        "    else:\n",
        "      run_steps = tf.constant(steps_remaining)\n",
        "    steps_remaining -= run_steps\n",
        "    step += run_steps\n",
        "\n",
        "    loss, img = deepdream(img, run_steps, tf.constant(step_size))\n",
        "    \n",
        "    display.clear_output(wait=True)\n",
        "    show(deprocess(img))\n",
        "    print (\"Step {}, loss {}\".format(step, loss))\n",
        "\n",
        "\n",
        "  result = deprocess(img)\n",
        "  display.clear_output(wait=True)\n",
        "  show(result)\n",
        "\n",
        "  return result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tEfd00rr0j8Z"
      },
      "outputs": [],
      "source": [
        "dream_img = run_deep_dream_simple(img=original_img, \n",
        "                                  steps=100, step_size=0.01)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2PbfXEVFNbGp"
      },
      "source": [
        "## Повышение октавы\n",
        "\n",
        "Довольно неплохо, но с этой первой попыткой есть несколько проблем:\n",
        "\n",
        "   1. Выходной сигнал зашумлен(это можно исправить с помощью функции потерь `tf.image.total_variation`).\n",
        "   1. Изображение низкого разрешения.\n",
        "   1. Создается впечатление, что все шаблоны создаются с одинаковой степенью детализации.\n",
        "  \n",
        "Один из подходов, который решает все эти проблемы, - это применение градиентного подъема на разных уровнях. Это позволит объединить шаблоны, созданные в меньших масштабах, в шаблоны более высоких масштабов и заполнить их дополнительными деталями.\n",
        "\n",
        "Для этого вы можете выполнить предыдущий подход градиентного подъема, затем увеличить размер изображения(который называется октавой) и повторить этот процесс для нескольких октав.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0eGDSdatLT-8"
      },
      "outputs": [],
      "source": [
        "import time\n",
        "start = time.time()\n",
        "\n",
        "OCTAVE_SCALE = 1.30\n",
        "\n",
        "img = tf.constant(np.array(original_img))\n",
        "base_shape = tf.shape(img)[:-1]\n",
        "float_base_shape = tf.cast(base_shape, tf.float32)\n",
        "\n",
        "for n in range(-2, 3):\n",
        "  new_shape = tf.cast(float_base_shape*(OCTAVE_SCALE**n), tf.int32)\n",
        "\n",
        "  img = tf.image.resize(img, new_shape).numpy()\n",
        "\n",
        "  img = run_deep_dream_simple(img=img, steps=50, step_size=0.01)\n",
        "\n",
        "display.clear_output(wait=True)\n",
        "img = tf.image.resize(img, base_shape)\n",
        "img = tf.image.convert_image_dtype(img/255.0, dtype=tf.uint8)\n",
        "show(img)\n",
        "\n",
        "end = time.time()\n",
        "end-start"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s9xqyeuwLZFy"
      },
      "source": [
        "## Опционально: масштабирование с помощью фрагментов\n",
        "\n",
        "Следует учитывать, что по мере увеличения размера изображения увеличиваются время и память, необходимые для вычисления градиента. Вышеупомянутая реализация повышения октав не будет работать с очень большими изображениями или с большим количеством октав.\n",
        "\n",
        "Чтобы избежать этой проблемы, вы можете разделить изображение на фрагменты и вычислить градиент для каждого фрагмента.\n",
        "\n",
        "Применение случайных сдвигов к изображению перед каждым вычислением мозаики предотвращает появление стыков фрагментов.\n",
        "\n",
        "Начнем с реализации случайного сдвига:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oGgLHk7o80ac"
      },
      "outputs": [],
      "source": [
        "def random_roll(img, maxroll):\n",
        "  # Произвольно сдвигаем изображение, чтобы избежать мозаичных стыков.\n",
        "  shift = tf.random.uniform(shape=[2], minval=-maxroll, maxval=maxroll, dtype=tf.int32)\n",
        "  shift_down, shift_right = shift[0],shift[1] \n",
        "  img_rolled = tf.roll(tf.roll(img, shift_right, axis=1), shift_down, axis=0)\n",
        "  return shift_down, shift_right, img_rolled"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sKsiqWfA9H41"
      },
      "outputs": [],
      "source": [
        "shift_down, shift_right, img_rolled = random_roll(np.array(original_img), 512)\n",
        "show(img_rolled)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tGIjA3UhhAt8"
      },
      "source": [
        "Вот мозаичный эквивалент функции `deepdream`, определенной ранее:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "x__TZ0uqNbGm"
      },
      "outputs": [],
      "source": [
        "class TiledGradients(tf.Module):\n",
        "  def __init__(self, model):\n",
        "    self.model = model\n",
        "\n",
        "  @tf.function(\n",
        "      input_signature=(\n",
        "        tf.TensorSpec(shape=[None,None,3], dtype=tf.float32),\n",
        "        tf.TensorSpec(shape=[], dtype=tf.int32),)\n",
        "  )\n",
        "  def __call__(self, img, tile_size=512):\n",
        "    shift_down, shift_right, img_rolled = random_roll(img, tile_size)\n",
        "\n",
        "    # Устанавливаем нулевые градиенты изображения.\n",
        "    gradients = tf.zeros_like(img_rolled)\n",
        "    \n",
        "    # Пропускаем последний фрагмент, если только он не один.\n",
        "    xs = tf.range(0, img_rolled.shape[0], tile_size)[:-1]\n",
        "    if not tf.cast(len(xs), bool):\n",
        "      xs = tf.constant([0])\n",
        "    ys = tf.range(0, img_rolled.shape[1], tile_size)[:-1]\n",
        "    if not tf.cast(len(ys), bool):\n",
        "      ys = tf.constant([0])\n",
        "\n",
        "    for x in xs:\n",
        "      for y in ys:\n",
        "        # Расчитываем градиент для фрагмента\n",
        "        with tf.GradientTape() as tape:\n",
        "          # Для этого нужны градиенты относительно `img_rolled`.\n",
        "          # По умолчанию `GradientTape` отслеживает только `tf.Variable`.\n",
        "          tape.watch(img_rolled)\n",
        "\n",
        "          # Извлекаем фрагмент из изображения.\n",
        "          img_tile = img_rolled[x:x+tile_size, y:y+tile_size]\n",
        "          loss = calc_loss(img_tile, self.model)\n",
        "\n",
        "        # Обновляем градиент изображения для этого фрагмента\n",
        "        gradients = gradients + tape.gradient(loss, img_rolled)\n",
        "\n",
        "    # Отменяем случайный сдвиг, примененный к изображению и его градиентам.\n",
        "    gradients = tf.roll(tf.roll(gradients, -shift_right, axis=1), -shift_down, axis=0)\n",
        "\n",
        "    # Нормализуем градиенты\n",
        "    gradients /= tf.math.reduce_std(gradients) + 1e-8 \n",
        "\n",
        "    return gradients "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Vcq4GubA2e5J"
      },
      "outputs": [],
      "source": [
        "get_tiled_gradients = TiledGradients(dream_model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hYnTTs_qiaND"
      },
      "source": [
        "Объединение всего этого дает масштабируемую реализацию deepdream с учетом октавы:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gA-15DM4NbGk"
      },
      "outputs": [],
      "source": [
        "def run_deep_dream_with_octaves(img, steps_per_octave=100, step_size=0.01, \n",
        "                                octaves=range(-2,3), octave_scale=1.3):\n",
        "  base_shape = tf.shape(img)\n",
        "  img = tf.keras.preprocessing.image.img_to_array(img)\n",
        "  img = tf.keras.applications.inception_v3.preprocess_input(img)\n",
        "\n",
        "  initial_shape = img.shape[:-1]\n",
        "  img = tf.image.resize(img, initial_shape)\n",
        "  for octave in octaves:\n",
        "    # Масштабируем изображение по октаве\n",
        "    new_size = tf.cast(tf.convert_to_tensor(base_shape[:-1]), tf.float32)*(octave_scale**octave)\n",
        "    img = tf.image.resize(img, tf.cast(new_size, tf.int32))\n",
        "\n",
        "    for step in range(steps_per_octave):\n",
        "      gradients = get_tiled_gradients(img)\n",
        "      img = img + gradients*step_size\n",
        "      img = tf.clip_by_value(img, -1, 1)\n",
        "\n",
        "      if step % 10 == 0:\n",
        "        display.clear_output(wait=True)\n",
        "        show(deprocess(img))\n",
        "        print (\"Octave {}, Step {}\".format(octave, step))\n",
        "    \n",
        "  result = deprocess(img)\n",
        "  return result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "T7PbRLV74RrU"
      },
      "outputs": [],
      "source": [
        "img = run_deep_dream_with_octaves(img=original_img, step_size=0.01)\n",
        "\n",
        "display.clear_output(wait=True)\n",
        "img = tf.image.resize(img, base_shape)\n",
        "img = tf.image.convert_image_dtype(img/255.0, dtype=tf.uint8)\n",
        "show(img)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0Og0-qLwNbGg"
      },
      "source": [
        "Намного лучше! Поиграйтесь с количеством октав, октавной шкалой и активированными слоями, чтобы изменить внешний вид вашего изображения в DeepDream.\n",
        "\n",
        "Вас также может заинтересовать [TensorFlow Lucid](https://github.com/tensorflow/lucid), который расширяет идеи, представленные в этом руководстве, для визуализации и интерпретации нейронных сетей."
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "deepdream.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
